#ifndef __MY_DATA__
#define __MY_DATA__

#include <unordered_map> // 哈希表
#include <pthread.h>     // pthread线程库
#include "util.hpp"
#include "config.hpp"

// 每个文件都有两个路径一个是real_path（当这个文件是热点文件，文件就在这个路径下）
// 第二个路径是pack_path（当这个文件是非热点文件，文件就在这个路径下），下载时需要先解压到real_path

namespace cloud
{
    typedef struct BackupInfo_t // 备份文件信息结构体
    {
        bool pack_flag;        // 压缩标志位，true为压缩，false为未压缩
        size_t fsize;          // 文件大小
        time_t mtime;          // 文件最后修改时间
        time_t atime;          // 文件随后访问时间
        std::string real_path; // 如果文件是热点文件，路径为real_path
        std::string pack_path; // 如果文件是非热点文件，将其压缩，路径为pack_path
        std::string url;       // 请求下载文件的资源定位符

        // 填充BackupInfo结构体
        bool NewBackupInfo(const std::string &realpath)
        {
            FileUtil fu(realpath);
            if (fu.Exists() == false)
            {
                std::cout << "new backupinfo: file not exists!\n";
                return false;
            }
            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packsuffix = config->GetPackFileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            this->pack_flag = false;
            this->fsize = fu.FileSize();
            this->mtime = fu.LastMTime();
            this->atime = fu.LastATime();
            this->real_path = realpath;
            this->pack_path = packdir + fu.FileName() + packsuffix;
            this->url = download_prefix + fu.FileName();
            return true;
        }
    } BackupInfo;

    class DataManager
    {
    private:
        std::string _backup_file;                           // 持久化存储：使用Json序列化将所有备份文件信息保存在文件中
        pthread_rwlock_t _rwlock;                           // 读写锁：读同步，写互斥，写时等待所有读写锁释放
        std::unordered_map<std::string, BackupInfo> _table; // 哈希表<url,BackupInfo>，通过请求下载文件的资源定位符获取文件信息
    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            pthread_rwlock_init(&_rwlock, nullptr); // 初始化读写锁
            InitLoad();                             // 初始化数据管理模块，即从_backup_file文件中读取信息填充到哈希表
        }

        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock); // 销毁读写锁
        }

        // 向哈希表中插入BackupInfo对象
        bool Insert(const BackupInfo &info)
        {
            pthread_rwlock_wrlock(&_rwlock); // 申请写锁
            _table[info.url] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();
            return true;
        }

        // 更新哈希表中信息
        bool Update(const BackupInfo &info)
        {
            pthread_rwlock_wrlock(&_rwlock); // 申请写锁
            _table[info.url] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();
            return true;
        }

        // 通过url获取备份文件信息
        bool GetOneByURL(const std::string &url, BackupInfo *info)
        {
            pthread_rwlock_rdlock(&_rwlock); // 申请读锁
            auto it = _table.find(url);      // 因为url是key值，所以直接通过find进行查找
            if (it == _table.end())
            {
                pthread_rwlock_unlock(&_rwlock);
                return false;
            }
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        // 通过realpath获取备份文件信息
        bool GetOneByRealPath(const std::string &realpath, BackupInfo *info)
        {
            pthread_rwlock_rdlock(&_rwlock); // 申请读锁
            auto it = _table.begin();
            for (; it != _table.end(); it++)
            {
                if (it->second.real_path == realpath)
                {
                    *info = it->second;
                    pthread_rwlock_unlock(&_rwlock);
                    return true;
                }
            }
            pthread_rwlock_unlock(&_rwlock);
            return false;
        }

        // 获取所有备份文件信息
        bool GetAll(std::vector<BackupInfo> *arry)
        {
            pthread_rwlock_rdlock(&_rwlock);
            auto it = _table.begin();
            for (; it != _table.end(); ++it)
            {
                arry->push_back(it->second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        // 备份文件信息的持久化存储
        bool Storage()
        {
            // 获取所有数据
            std::vector<BackupInfo> arry;
            this->GetAll(&arry);

            // 添加数据到Json::Value
            Json::Value root;
            for (int i = 0; i < arry.size(); i++)
            {
                Json::Value item;
                item["pack_flag"] = arry[i].pack_flag;
                item["fsize"] = (Json::Int64)arry[i].fsize;
                // Json::Value中不包含time_t的数据类型
                item["atime"] = (Json::Int64)arry[i].atime;
                item["mtime"] = (Json::Int64)arry[i].mtime;
                item["real_path"] = arry[i].real_path;
                item["pack_path"] = arry[i].pack_path;
                item["url"] = arry[i].url;
                root.append(item); // 向root添加数组元素，数组元素类型为item
            }

            // 序列化root
            std::string body;
            JsonUtil::Serialize(root, &body);

            // 写入存储备份信息的文件
            FileUtil fu(_backup_file);
            fu.SetContent(body);
            return true;
        }

        // 数据管理模块的初始化
        bool InitLoad()
        {
            // 将数据从文件中读取
            FileUtil fu(_backup_file);
            if (fu.Exists() == false)
            {
                return true;
            }
            std::string body;
            fu.GetContent(&body);

            // 反序列化
            Json::Value root;
            JsonUtil::UnSerialize(body, &root);

            // 将反序列化得到的Json::Value中的数据添加到table中
            for (int i = 0; i < root.size(); i++)
            {
                BackupInfo info;
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.fsize = root[i]["fsize"].asInt64();
                info.atime = root[i]["atime"].asInt64();
                info.mtime = root[i]["mtime"].asInt64();
                info.pack_path = root[i]["pack_path"].asString();
                info.real_path = root[i]["real_path"].asString();
                info.url = root[i]["url"].asString();
                Insert(info);
            }
            return true;
        }
    };
}

#endif